//
// EpollServer.h
//
// $Id: //poco/1.4/Net/include/Poco/Net/EpollServer.h#2 $
//
// Library: Net
// Package: Sockets
// Module:  EpollServer
//
// Definition of the EpollServer class.
//
// Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier:	BSL-1.0
//


#ifndef Net_EPollServer_INCLUDED
#define Net_EPollServer_INCLUDED


#include "Poco/Net/Net.h"
#include "Poco/Net/Socket.h"
#include "Poco/Mutex.h"
#include "Poco/Runnable.h"
#include "Poco/Thread.h"
#include "Poco/Net/EpollDispatcher.h"
#include "Poco/Thread.h"
#include <set>
#include <sys/epoll.h>
#include <deque>


namespace Poco {
namespace Net {

class FdTimeOutThread;	

class Net_API EpollServer: public Socket
	/// This class provides an interface to a
	/// TCP server socket.
{
public:
	
	EpollServer(const SocketAddress& address, EpollDispatcher& dispather,bool reuseAddress = true,EPOLL_MODE mode = EPOLLMODE_ET,int backlog = LISTEN_BACKLOG);
		/// when reuseAddress == true,reuse the port and address
		/// Creates a server socket, binds it
		/// to the given address and puts it in listening
		/// state.
		///
		/// After successful construction, the server socket
		/// is ready to accept connections.

	EpollServer(Poco::UInt16 port, EpollDispatcher& dispather,bool reuseAddress = true,EPOLL_MODE mode = EPOLLMODE_ET,int backlog = LISTEN_BACKLOG);
		/// when reuseAddress == true,reuse the port and address
		/// Creates a server socket, binds it
		/// to the given port and puts it in listening
		/// state.
		///
		/// After successful construction, the server socket
		/// is ready to accept connections.
	
	virtual ~EpollServer();
		/// Destroys the StreamSocket.
	
	void wait();
		/// epoll wait

	void stopWait()
	{
		if (!m_exitflag)
		{
			m_exitflag = true;
			m_dispatcher.stop();
		}
	}
	
	int acceptConnection();
		/// Get the next completed connection from the
		/// socket's completed connection queue.
		///
		/// If the queue is empty, waits until a connection
		/// request completes.
		///
		/// Returns a new TCP socket for the connection
		/// with the client.
	
	///	void setTimeoutFds(const std::set<int>& timeoutFds);
		/// an interface for server to close fds
		/// auto called by m_dispatcher
	
	void addTimeoutFd(int timeoutFd);
		/// an interface for server to close fds
		/// auto called by m_dispatcher
	
	/// monitor info
	bool running() const
	{
		return !m_exitflag;
	}
	
	int closingFdSize() const
	{
		ScopedLock<Mutex>	lock(const_cast<Mutex&>(m_timeoutFdsMutex));
		return m_timeoutFds.size();
	}
	
	bool isEdegTrigerMode()
	{
		return m_mode == EPOLLMODE_ET;
	}
private:
	EpollServer& operator = (const Socket& socket);
	void getTimeoutFds(std::set<int>& timeoutFds);
		/// get a copy of closing timeoutfds;
	friend class FdTimeOutThread;
	
private:
	void init(UInt32 maxopenfd = MAXOPENFD);
	int					m_epollFd;
		/// initialize epoll
	bool 				m_exitflag;
		/// exit control
	EpollDispatcher&	m_dispatcher;
		/// EpollDispatcher
	std::set<int>		m_timeoutFds;
		/// timeout fds
	Mutex				m_timeoutFdsMutex;
	Thread				m_timeoutThread;
	int					m_maxevent;
	unsigned int		m_event;
	FdTimeOutThread*	m_timeoutTarget;
	EPOLL_MODE 			m_mode;
};

class FdTimeOutThread : public Runnable
{
public:
    FdTimeOutThread(EpollServer& server, int epollFd,EpollDispatcher& dispather)
	:m_server(server),
	m_epollFd(epollFd),
	m_dispatcher(dispather),
	m_exitflag(false)
	{
		
	}
	
	virtual void run()
	{
		while(!m_exitflag)
		{
			/// 主动关闭超时连接
			std::set<int>	timeoutFds;
			m_server.getTimeoutFds(timeoutFds);
			struct epoll_event 			ev;
			ev.data.fd  				= 0;
			ev.events   				= m_server.m_event;
			for(auto fd:timeoutFds)
			{
				if (ev.events == 0)
					break;
				ev.data.fd = fd;
				epoll_ctl(m_epollFd,EPOLL_CTL_DEL,fd,&ev);
				poco_closesocket(fd);
				--m_server.m_maxevent;
				m_dispatcher.dequeue(fd);
			}
			if (timeoutFds.empty())
			{
				Thread::sleep(1000);
			}
		}
	}
	void stop()
	{
		m_exitflag = true;
	}
private:
	EpollServer&			m_server;
	int 					m_epollFd;
	EpollDispatcher&		m_dispatcher;
	bool					m_exitflag;
};

} } // namespace Poco::Net


#endif // Net_EPollServer_INCLUDED
